using System;
using System.IO;
using System.Diagnostics;
using Tao.Sdl;
using Tao.DevIl;
using Tao.OpenGl;

namespace Dida
{
	public class GlTexture : UnmanagedResource
	{
		int glTexture = -1;
		int width;
		int height;
		
		/// <summary>
		/// Creates a new texture from the given image file stream.
		/// </summary>
		/// <remarks>
		/// The image is loaded using Tao.DevIl, so it supports allformats that 
		/// are supported by DevIl.
		/// </remarks>
		public GlTexture(Stream src)
			: base(SdlBinding.UnmanagedResourceCategory)
		{
			byte[] bytes = new byte[src.Length];
			src.Read(bytes,0,(int)src.Length);

			int ilImage = -1;
			try
			{
				// wtf, what is this some silly kind of library. I guess they thought
				// OpenGl was cool, but that shouldn't be a reason to imitate their 
				// state machine syntax wherever possible.
				// Well, I'll use it anyway, because it's part of Tao, and this way I can keep
				// the number of dependencies of Dida low.
				Il.ilGenImages(1, out ilImage);
				Il.ilBindImage(ilImage);
				if (!Il.ilLoadL(Il.IL_TYPE_UNKNOWN, bytes, (int)src.Length))
					throw new Exception("Failed to load a texture");
				
				width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
				height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
				
				byte[] pixels = new byte[width * height * 4];
				Il.ilCopyPixels(0, 0, 0, width, height, 1, Il.IL_RGBA, Il.IL_BYTE, pixels);
								
				Gl.glGenTextures(1, out glTexture);
				Gl.glBindTexture(Gl.GL_TEXTURE_2D, glTexture);
				Glu.gluBuild2DMipmaps(Gl.GL_TEXTURE_2D, 4, width, height,
					Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, pixels);
				Debug.Assert(Gl.glGetError() == Gl.GL_NO_ERROR,
					Glu.gluErrorString(Gl.glGetError()));

				Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
				Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);

				GC.AddMemoryPressure(width * height * 4);

			}
			finally
			{
				// the image was only temporary stuff, so we have to delete it in 
				// normal cases, and in cases of an exception.

				if (ilImage != -1)
				{
					Il.ilBindImage(0);
					Il.ilDeleteImages(1, ref ilImage);

					ilImage = -1;
				}
			}
		}

		protected override void FreeUnmanagedResources()
		{
			if (glTexture != -1)
			{
				Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
				Gl.glDeleteTextures(1, ref glTexture);
				glTexture = -1;

				GC.RemoveMemoryPressure(width * height * 4);
			}
		}

		public void Bind()
		{
			Debug.Assert(glTexture != -1);

			Gl.glBindTexture(Gl.GL_TEXTURE_2D, glTexture);
		}
	}
}